<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      /**
       * 函数扩展
      */
    //  rest 参数
    let {log} = console
    function add(...values){
      let sum = 0

      for(var val of values){
        sum += val
      }

      return sum
    }

    log( add(2, 5,3))

    // rest参数代替arguments
    // arguments 变量的写法
    function sortNumbers(){
      return Array.prototype.slice
      .call(arguments).sort()
    }
    // rest 参数的写法
    const sortNumbers1 =(...numbers) => numbers.sort() 

    // 利用rest 参数改写数组 push 方法的例子
    function push(array,...items){
      items.forEach(function(item){
        array.push(item)
        console.log(item)
      })
    }

    var a =[]
    push (a,1,2,3)
    log(a)

    // name 属性   返回该函数的函数名
    function foo() {}
    log(foo.name)
    // 具名函数返回函数本名
    const bar = function baz(){}
    log(bar.name)

    /**
     * 箭头函数
    */
    var f = v => v

    // 等同于
    // var f = function (v){ return v }

    /**
     * 尾调用
     * 指某个函数的“最后一步”是调用另一个函数
    */
    function fu(x){
      return g(x)
    }

    // 不属于尾调用的
    // 一
    function fu1(x){
      let y = g(x)
      return y
    }

    // 二
    function fu2(x){
      return g(x) +1
    }

    // 三
    function fu3(x){
      g(x)
    }

    // 三 等同于
    /**
     * function fu3(){
     *    g(x)
     *   return undefined
     * }
    */


    // 尾递归
    // 函数调用自身，称为递归，尾调用自身，就称为尾递归

    // 阶乘函数，计算 n 的阶乘，最多需要保存 n 个调用记录，复杂度O(n) 
    function factorial(n){
      if(n===1) return 1
      return n* factorial(n-1)
    }
    log(factorial(5))

    // 改写成尾递归,只保留一个调用记录，复杂度O(1)
    function factorial2(n,total){
      if(n === 1) return total
      return factorial2(n-1,n*total)
    }
    log(factorial2(5,1))

    // Fibonacci 数列
    function Fibonacci (n){
      if(n<=1) return 1
      return Fibonacci(n-1) + Fibonacci(n-2)
    }
    log(Fibonacci(10))
    // log(Fibonacci(100))  //超时

    // 尾递归优化Fibonacci 数列
    function Fibonacci2(n,ac1 =1,ac2 =1){
      if(n<=1) {return ac2}
      return Fibonacci2 (n-1,ac2,ac1+ac2)
    }
    log(Fibonacci2(100))

    /**
     * 数组的扩展
    */
    // 扩展运算符
    // 将数组转为逗号分隔的参数序列
    log(...[1,2,3])
    log(1,...[2,3,4],5)

    // 主要用于函数调用
    function push2 (array,...items){
      array.push2(...items)
    }
    function add2(x,y){
      return x+y
    }
    const numbers = [4,38]
    log(add2(...numbers))

    // 后面放置表达式
    let x =0
    const arr = [
      ...(x>0?["a"]:[]),
      "a"
    ]
    log(arr)

    // 复制数组
    const a1 = [1,2]
    const a2 = a1
    a2[0] = 2
    log(a1)

    const b1 =  [1,2]
    const b2 = [...b1]
    const [...b3] = b1
    log(b2)
    log(b3)

    // 合并数组
    const arr1 = ["a","b"]
    const arr2 = ["c"]
    const arr3 = ["d","e"]
    log([...arr1,...arr2,...arr3])

    // 与解构赋值结合
    const [first,...rest] = [1,2,3,4,5]
    log(first)
    log(rest)

    const [first1,...rest1] = []
    log(first1)
    log(rest1)

    const [first2,...rest2] = ["foo"]
    log(first2)
    log(rest2)
    log([..."hello"])

    // Array.from()
    // 用于将类似数组的对象和可遍历的对象转为真正的数组
    let arrayLike = {
      "0":'a',
      "1":"b",
      "2":"c",
      length:3
    }
    let arr4 = Array.from(arrayLike)
    log(arr4)
    const a5 = "sda"
    console.log(a5.prototype);
    </script>
  </body>
</html>
